'use strict';
const Service = require('egg').Service;
const assert = require('assert');
const bcrypt = require('bcrypt');

class User extends Service {
  async list({ offset = 0, limit = 10 }) {
    return this.ctx.model.User.findAndCountAll({
      offset,
      limit,
      order: [[ 'created_at', 'desc' ], [ 'id', 'desc' ]],
    });
  }

  async find(id) {
    const { User, UserRole } = this.ctx.model;
    const user = await User.findByPk(id, { include: { model: UserRole, as: 'roles' } });
    if (!user) {
      this.ctx.throw(404, 'user not found');
    }
    if (user.roles instanceof Array) {
      user.roles = user.roles.map(role => role.role);
    } else {
      user.roles = [ 'user' ];
    }
    return user;
  }

  async create(user) {
    const { username, password, roles = [ 'user' ] } = user;
    assert(username, '用户名必填');
    assert(password, '密码必填');
    user.password = this.encode(password);
    user.roles = roles.map(role => ({ role }));
    const { User, UserRole } = this.ctx.model;
    return User.create(user, { include: { model: UserRole, as: 'roles' } });
  }

  async update({ id, updates }) {
    const user = await this.ctx.model.User.findByPk(id);
    if (!user) {
      this.ctx.throw(404, 'user not found');
    }
    if (updates.password) {
      updates.password = this.encode(updates.password);
    }
    return user.update(updates);
  }

  async del(id) {
    const user = await this.ctx.model.User.findByPk(id);
    if (!user) {
      this.ctx.throw(404, 'user not found');
    }
    return user.destroy();
  }

  async verify(username, password) {
    const user = await this.ctx.model.User.findOne({ where: { username } });
    if (user && bcrypt.compareSync(password, user.password)) {
      return { id: user.id, username: user.username };
    }
    return null;
  }

  encode(password) {
    return bcrypt.hashSync(password, 10);
  }
}

module.exports = User;
